BlankClassSoDocsWillBeGenerated

version(D_Ddoc)
class BlankClassSoDocsWillBeGenerated

Examples

This example is for the FOREIGN KEY constraints. The tables in SQL can be created by $(D $(D sql CREATE TABLE Authors ( AuthorId INTEGER NOT NULL PRIMARY KEY, Name TEXT ); CREATE TABLE Books ( BookId INTEGER NOT NULL PRIMARY KEY, Title TEXT, AuthorId INTEGER, FOREIGN KEY (AuthorId) REFERENCES Authors(AuthorId) ); ))

The Books table now must have values in Books.AuthorId that are in Authors.AuthorId. By default if Authors deletes or updates its AuthorId and Books references the AuthorId, an exception is thrown. If you would like Books to cascade the effects instead you would create the Books table like $(D $(D sql CREATE TABLE Books ( BookId INTEGER NOT NULL PRIMARY KEY, Title TEXT, AuthorId INTEGER, FOREIGN KEY (AuthorId) REFERENCES Authors(AuthorId) ON DELETE CASCADE ON UPDATE CASCADE ); ))

This package can do any of the update or delete rules. For more rules information look at $(WIKI constraints, Rule).

Below I will create the classes. I will use the cascaded Books for this example.

1 import db_constraints;
2 
3 class Author
4 {
5     private int _AuthorId;
6     @PrimaryKeyColumn @NotNull
7     @property int AuthorId()
8     {
9         return _AuthorId;
10     }
11     @property void AuthorId(int value)
12     {
13         setter(_AuthorId, value);
14     }
15     private string _Name;
16     @property string Name()
17     {
18         return _Name;
19     }
20     @property void Name(string value)
21     {
22         setter(_Name, value);
23     }
24     this(int AuthorId_, string Name_)
25     {
26         this._AuthorId = AuthorId_;
27         this._Name = Name_;
28         initializeKeyedItem();
29     }
30 
31     // we must define dup() since we are going
32     // to change AuthorId which is our implied
33     // clustered index
34     Author dup()
35     {
36         return new Author(this._AuthorId, this._Name);
37     }
38 
39     mixin KeyedItem!();
40 }
41 class Authors
42 {
43     mixin KeyedCollection!(Author);
44 }
45 
46 // adding in this function so we can get multiple
47 // author records at once
48 Authors GetAuthorsFromDB()
49 {
50     return new Authors([
51                        new Author(1, "Jane Austen"),
52                        new Author(2, "Leo Tolstoy"),
53                        new Author(3, "Joseph Heller"),
54                        new Author(4, "Charles Dickens")
55                        ]);
56 }
57 
58 
59 // we could put Books in a different file
60 // to do that you would just need to import the file where Authors is.
61 
62 // attach the foreign key constraint attribute to the singular class
63 @ForeignKeyConstraint!(
64     ["AuthorId"], /* Book column */
65     "Authors", /* referenced table which is Authors in this case */
66     ["AuthorId"], /* referenced column which is Authors.AuthorId */
67     Rule.cascade, /* what to do when we update Authors.AuthorId */
68     Rule.cascade) /* what to do when we delete Authors.AuthorId */
69 class Book
70 {
71     private int _BookId;
72     @PrimaryKeyColumn @NotNull
73     @property int BookId()
74     {
75         return _BookId;
76     }
77     @property void BookId(int value)
78     {
79         setter(_BookId, value);
80     }
81     private string _Title;
82     @property string Title()
83     {
84         return _Title;
85     }
86     @property void Title(string value)
87     {
88         setter(_Title, value);
89     }
90     private int _AuthorId;
91     @property int AuthorId()
92     {
93         return _AuthorId;
94     }
95     @property void AuthorId(int value)
96     {
97         setter(_AuthorId, value);
98     }
99     this(int BookId_, string Title_, int AuthorId_)
100     {
101         this._BookId = BookId_;
102         this._Title = Title_;
103         this._AuthorId = AuthorId_;
104         initializeKeyedItem();
105     }
106 
107     mixin KeyedItem!();
108 }
109 
110 class Books
111 {
112     mixin KeyedCollection!(Book);
113 }
114 Books GetBooksFromDB()
115 {
116     return new Books([
117                      new Book(1, "Emma", 1),
118                      new Book(2, "War and Peace", 2),
119                      new Book(3, "Catch XII", 3),
120                      new Book(4, "David Copperfield", 4),
121                      new Book(5, "Good as Gold", 3),
122                      new Book(6, "Anna Karenia", 2)
123                      ]);
124 }
125 
126 
127 // we will get both collections
128 // and then associate authors to books
129 
130 // ON UPDATE CASCADE
131 {
132     auto authors = GetAuthorsFromDB();
133     auto books = GetBooksFromDB();
134 
135     // when we associate authors to books
136     // there should be no exceptions since
137     // we are starting with correct data
138     import std.exception : assertNotThrown, assertThrown;
139     assertNotThrown!ForeignKeyException(books.authors = authors);
140     // books.authors is a write-only property made by
141     // mixin KeyedCollection!(Book);
142     // you can also set books.authors = null when you want to
143     // remove the association
144 
145     // if you recall from before we can use the primary key to
146     // search our collections easily. In Books, Emma has BookId 1.
147     assert(books[1].Title == "Emma");
148     assert(books[1].BookId == 1);
149 
150     // Emma is written by Jane Austen and
151     // in authors Jane Austen has AuthorId 1
152     assert(authors[1].Name == "Jane Austen");
153     assert(authors.contains(books[1].AuthorId));
154     assert(authors[1].AuthorId == books[1].AuthorId);
155 
156     // lets say somehow we changed Jane Austen to have AuthorId 5
157     // since we have on update cascade for books we would expect
158     // Emma to get AuthorId 5
159     authors[1].AuthorId = 5;
160     // this will also change authors to no longer have key 1
161     assert(authors.contains(5) && !authors.contains(1));
162     // Emma still has BookId 1 since that did not change but
163     // should have AuthorId 5
164     assert(books[1].Title == "Emma");
165     assert(books[1].AuthorId == 5);
166 
167     // We were able to change Jane Austen's AuthorId since we
168     // defined dup in Author. We did not define dup in Books
169     // which means if we change BookId we should expect a
170     // KeyedException
171     assertThrown!KeyedException(books[1].BookId = 7);
172 
173     // it is good but not necessary to set books.authors to null when you
174     // leave scope just to disconnect signals and associations
175     books.authors = null;
176 }
177 
178 // ON DELETE CASCADE
179 {
180     auto authors = GetAuthorsFromDB();
181     auto books = GetBooksFromDB();
182     books.authors = authors;
183 
184     // we have 4 authors
185     assert(authors.length == 4);
186     // and 6 books
187     assert(books.length == 6);
188 
189     import std.algorithm : count;
190     // there are two books that have author id 3
191     auto booksWithAuthorId3 =
192         books.byValue.count!((a, b) => a.AuthorId == b)(3);
193 
194     assert(booksWithAuthorId3 == 2);
195 
196     // this means if we delete AuthorId 3 from authors and
197     // we have on delete cascade for books we should expect
198     // books to have length 4 and authors to have length 3
199     authors.remove(3);
200     assert(authors.length == 3);
201     assert(books.length == 4);
202 
203     booksWithAuthorId3 = books.byValue.count!((a, b) => a.AuthorId == b)(3);
204     assert(booksWithAuthorId3 == 0);
205 
206     books.authors = null;
207 }
208 
209 // ON DELETE RESTRICT
210 {
211     import std.exception : assertThrown, assertNotThrown;
212     auto authors = GetAuthorsFromDB();
213     auto books = GetBooksFromDB();
214     books.authors = authors;
215 
216     // you can change the on update and on delete rule for your foreign key
217     // by using the foreign key name and _UpdateRule or _DeleteRule...we
218     // did not name ours so it got the default fk_Book_Authors
219 
220     // lets say we want to restrict authors deletion now
221     books.fk_Book_Authors_DeleteRule = Rule.restrict;
222 
223     // since we have 2 records in books that reference AuthorId 3 we should
224     // get an exception when deleting AuthorId 3 from authors
225     assertThrown!ForeignKeyException(authors.remove(3));
226 
227     // now we can unreference our table and remove 3 without errors
228     books.authors = null;
229     assertNotThrown!ForeignKeyException(authors.remove(3));
230 
231     // but now when we try to re-associate we will get an exception
232     assertThrown!ForeignKeyException(books.authors = authors);
233 }

Meta